package com.epam.mentoring.music.parser;

import com.thoughtworks.xstream.mapper.Mapper;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.MalformedInputException;
import java.util.ArrayList;
import java.util.List;

public class ChartsParser {

    private URL dataSourceURL;
    private File inputFile;
    private File stylesheetFile;
    private File schemaFile;
    private File outputFile;

    public ChartsParser(String dataSourceURL, String xslFile){
        try {
            this.dataSourceURL = new URL(dataSourceURL);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        inputFile = new File(System.getProperty("user.dir") + "/src/main/resources/com/epam/mentoring/music/parser/" + "in.xml");
        stylesheetFile = new File(System.getProperty("user.dir") + "/src/main/resources/com/epam/mentoring/music/parser/" + xslFile);
        schemaFile = new File(System.getProperty("user.dir") + "/src/main/resources/com/epam/mentoring/music/parser/" + "tracks_output.xsd");
        outputFile = new File(inputFile.getAbsolutePath().substring(0,inputFile.getAbsolutePath().lastIndexOf(File.separator)) + File.separator + inputFile.getName().replace(".", "_out."));
    }

    /**
     * Getting XML from WEB and transforming it to unified format
     *
     *
     * @return tracks
     */
    public List<MusicTrack> getLatestTracks() {
        try {
            /* reading XML from web */
            System.out.println("Getting data from WEB and writing to " + inputFile + "...");
            URLConnection con = dataSourceURL.openConnection();
            BufferedInputStream bis = new BufferedInputStream(con.getInputStream());
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(inputFile));
            int i;
            while ((i = bis.read()) != -1) {
                bos.write(i);
            }
            bos.flush();
            bis.close();
        } catch (MalformedInputException malformedInputException) {
            malformedInputException.printStackTrace();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }

        /* XML transformation and validation */
        List<MusicTrack> tracks = null;
        try {
            System.out.println("Transformation...");

            // Parsing XML to DOM
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(true);
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.parse(inputFile);

            // Use a Transformer for output
            TransformerFactory tFactory = TransformerFactory.newInstance();
            StreamSource stylesource = new StreamSource(stylesheetFile);
            Transformer transformer = tFactory.newTransformer(stylesource);

            DOMSource source = new DOMSource(document);
            StreamResult result = new StreamResult(outputFile);
            transformer.transform(source, result);

            // Validation
            SchemaFactory sFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
            Schema schema = sFactory.newSchema(schemaFile);
            Validator validator = schema.newValidator();
            validator.validate(new StreamSource(outputFile));
            System.out.println(outputFile.getName() + " is valid");

            // Get elements (tracks) and populate list of MusicTracks
            tracks = new ArrayList();
            Document tracksDocument = builder.parse(outputFile);
            XPath xPath = XPathFactory.newInstance().newXPath();
            NodeList trackNodes = (NodeList) xPath.evaluate("/tracks/track", tracksDocument, XPathConstants.NODESET);
            if (trackNodes != null && trackNodes.getLength() > 0) {
                for (int i = 0; i < trackNodes.getLength(); i++) {
                    Node trackNode = trackNodes.item(i);
                    Node name = (Node) xPath.evaluate("name", trackNode, XPathConstants.NODE);
                    Node artistName = (Node) xPath.evaluate("artistName", trackNode, XPathConstants.NODE);
                    Node albumName = (Node) xPath.evaluate("albumName", trackNode, XPathConstants.NODE);
                    Node infoURL = (Node) xPath.evaluate("URL", trackNode, XPathConstants.NODE);

                    String atrAlbumName;
                    if ( null == albumName || albumName.getFirstChild().getNodeValue().equals("") ) {
                       atrAlbumName = "Default album";
                    } else atrAlbumName = albumName.getFirstChild().getNodeValue();

                    Node imageURL = (Node) xPath.evaluate("image", trackNode, XPathConstants.NODE);
                    String atrImageURL;
                    if (  null != imageURL.getFirstChild()) {
                        atrImageURL = imageURL.getFirstChild().getNodeValue();
                    } else atrImageURL = "https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcSeHn8jfc6MCTZiTy15hQ74edS4azIHbzogLIbPjRVqdQmonAhaPA";

                     tracks.add(new MusicTrack(name.getFirstChild().getNodeValue().replace("'", "''"), artistName.getFirstChild().getNodeValue().replace("'", "''"), atrAlbumName.replace("'", "''"), atrImageURL.replace("'", "''"), infoURL.getFirstChild().getNodeValue().replace("'", "''")));
                     //System.out.println(name.getFirstChild().getNodeValue());

                }
            }

        } catch (SAXException e) {
            System.out.println("Validation or transformation failed");
            e.printStackTrace();
        } catch (ParserConfigurationException pce) {
            pce.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } catch (TransformerException te) {
            te.printStackTrace();
        } catch (XPathExpressionException xe) {
            System.out.println("Wrong xpath");
            xe.printStackTrace();
        }

        return tracks;

    }

}
